#include "RingBufferV1.h"
#include <algorithm>
#include <cstddef>
using std::size_t;
namespace xncc {
namespace msg {
    void* RingBuffer::PrepareWrite(size_t size, size_t alignment)
    {
        size_t pos = Align(m_Writer.pos, alignment);
        size_t end = pos + size;
        if (end > m_Writer.end)
            GetBufferSpaceToWriteTo(pos, end);
        m_Writer.pos = end;
        return m_Writer.buffer + pos;
    }

    void RingBuffer::FinishWrite()
    {
        m_WriterShared.pos.store(m_Writer.base + m_Writer.pos, std::memory_order_release);
    }

    void* RingBuffer::PrepareRead(size_t size, size_t alignment)
    {
        size_t pos = Align(m_Reader.pos, alignment);
        size_t end = pos + size;
        if (end > m_Reader.end)
            GetBufferSpaceToReadFrom(pos, end);
        m_Reader.pos = end;
        return m_Reader.buffer + pos;
    }

    void RingBuffer::FinishRead() { m_ReaderShared.pos.store(m_Reader.base + m_Reader.pos, std::memory_order_release); }

    void RingBuffer::GetBufferSpaceToWriteTo(size_t& pos, size_t& end)
    {
        if (end > m_Writer.size) {
            end -= pos;
            pos = 0;
            m_Writer.base += m_Writer.size;
        }
        for (;;) {
            size_t readerPos = m_ReaderShared.pos.load(std::memory_order_acquire);
            size_t available = readerPos - m_Writer.base + m_Writer.size;
            // Signed comparison (available can be negative)
            if (static_cast<ptrdiff_t>(available) >= static_cast<ptrdiff_t>(end)) {
                m_Writer.end = std::min(available, m_Writer.size);
                break;
            }
        }
    }

    void RingBuffer::GetBufferSpaceToReadFrom(size_t& pos, size_t& end)
    {
        if (end > m_Reader.size) {
            end -= pos;
            pos = 0;
            m_Reader.base += m_Reader.size;
        }
        for (;;) {
            size_t writerPos = m_WriterShared.pos.load(std::memory_order_acquire);
            size_t available = writerPos - m_Reader.base;
            // Signed comparison (available can be negative)
            if (static_cast<ptrdiff_t>(available) >= static_cast<ptrdiff_t>(end)) {
                m_Reader.end = std::min(available, m_Reader.size);
                break;
            }
        }
    }
}  // namespace msg
}  // namespace xncc